using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using NBusiness.CodeDom;
using NBusiness.CodeDom.Compiler;
using NBusiness.ESharp.Compiler;
using NBusiness.CodeDom.Services;

namespace NBusiness.ESharp
{
    [Serializable]
    public class ESharpValidation : EntityValidation
    {
        [NonSerialized]
        EntityValidation _matched;
        internal EntityValidation Matched { get { return _matched; } }

        protected override void Attach()
        {
            foreach (EntityElement element in Elements)
            {
                if (element.Type == ESharpEntityElementTypes.ValidateProperty)
                {
                    IValidatable[] found = (Parent as ESharpEntity).FindValidatable(element.Value);

                    if (found.Length == 0)
                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5001, element));
                    else if (found.Length > 1)
                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5004, element));
                    else
                        Validatable = found[0];
                }
                else if (element.Type == ESharpEntityElementTypes.ValidateType)
                {
                    List<EntityUsing> usingStatements = new List<EntityUsing>();
                    if ((Parent.Parent as EntityFamily).Root.Using.ContainsKey(element.Path ?? ""))
                        usingStatements.AddRange((Parent.Parent as EntityFamily).Root.Using[element.Path ?? ""]);
                    
                    EntityUsing thisNamespace = new ESharpUsing();
                    thisNamespace.Namespace = (Parent.Parent as EntityFamily).Fullname;
                    thisNamespace.Root = (Parent.Parent as EntityFamily).Root;
                    usingStatements.Add(thisNamespace);

                    IFrameworkService framework = Provider.GetService(typeof(IFrameworkService)) as IFrameworkService;
                    EntityValidation[] rules = framework.GetValidationRules();
                    
                    string name = element.Value.ToLower();
                    var found = from r in rules
                                from u in usingStatements
                                where
                                    r.Name.ToLower() == (u.Namespace.ToLower() + "." + name.ToLower()) ||
                                    r.Name.ToLower() == (u.Namespace.ToLower() + "." + name.ToLower() + "validationrule")
                                select r;

                    int count = found.Count();
                    if (count == 0)
                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5002, element));
                    else if (count > 1)
                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5004, element));
                    else
                    {
                        _matched = found.First() as EntityValidation;
                        Name = _matched.Name;
                    }
                }
            }
        }

        protected override void InitializeAssignmentLogic()
        {
            Assignments.Add(new AssignmentLogic(ESharpEntityElementTypes.ValidateProperty));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.ValidateType,
                SetName));

            Assignments.Add(new AssignmentLogic(ESharpEntityElementTypes.ValidateArgsBegin));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.ValidateArgValue,
                AddValue));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.ValidateArgName,
                AddArg));

            Assignments.Add(new AssignmentLogic(ESharpEntityElementTypes.ValidateArgSplit));

            Assignments.Add(new AssignmentLogic(ESharpEntityElementTypes.ValidateArgsEnd));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.StatementEnd,
                PostAssignmentAction.Stop));

            Assignments.Add(new AssignmentLogic(ESharpEntityElementTypes.WhiteSpace));
            Assignments.Add(new AssignmentLogic(ESharpEntityElementTypes.Comment));
        }

        private void SetName(object sender, AssignmentLogicEventArgs e)
        {
            Name = e.Elements.Peek().Value;
        }

        private void AddArg(object sender, AssignmentLogicEventArgs e)
        {
            ESharpValidationArgument arg = new ESharpValidationArgument();
            Arguments.Add(arg);
            arg.Parent = this;
            arg.Name = e.Elements.Peek().Value;
            (arg as ICompileUnit).Build(e.Elements, ESharpEntityElementTypes.ValidateArgName);
        }

        private void AddValue(object sender, AssignmentLogicEventArgs e)
        {
            ESharpValidationArgument arg = new ESharpValidationArgument();
            Arguments.Add(arg);
            arg.Parent = this;
            arg.Value = e.Elements.Peek().Value.Trim('"');
        }
    }
}
